package com.tn.openrpc.jms.secure;

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.TextMessage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.AuthenticationException;

import com.tn.openrpc.channel.secure.SecureRemoteChannelRequest;
import com.tn.openrpc.jms.RemoteChannelRequestProcessor;
import com.tn.openrpc.serialization.SerializationException;
import com.tn.openrpc.serialization.Serializer;
import com.tn.openrpc.util.ParametrizedFactory;

/**
 * A specialization of <code>RemoteChannelRequestProcessor</code> that performs authentication.
 */
public class SecureRemoteChannelRequestProcessor extends RemoteChannelRequestProcessor
{
  private static final Logger LOGGER = LoggerFactory.getLogger(SecureRemoteChannelRequestProcessor.class);

  private AuthenticationProvider authenticationProvider;

  /**
   * Creates a new <code>RemoteChannelRequestProcessor</code>.
   *
   * @param authenticationProvider the provider that performs the authentication.
   * @param connectionFactory      the connection factory used to connect to the destinations.
   * @param serializer             the serializer used to serialize requests and de-serialize the responses.
   * @param targetFactory          the factory that is used to create (or look-up) the target.
   */
  public SecureRemoteChannelRequestProcessor(
    AuthenticationProvider authenticationProvider,
    ConnectionFactory connectionFactory,
    Serializer<String> serializer,
    ParametrizedFactory<Object, String> targetFactory
  )
  {
    super(connectionFactory, serializer, targetFactory);
    this.authenticationProvider = authenticationProvider;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected Object getRequestObject(TextMessage message) throws JMSException, SerializationException
  {
    Object request = getSerializer().deserialize(message.getText());

    if (!(request instanceof SecureRemoteChannelRequest))
    {
      LOGGER.error("Unhandled request type encountered: {}", request);
      return null;
    }

    SecureRemoteChannelRequest secureRemoteChannelRequest = (SecureRemoteChannelRequest)request;

    try
    {
      authenticationProvider.authenticate(secureRemoteChannelRequest.getAuthentication());
      return secureRemoteChannelRequest;
    }
    catch (AuthenticationException e)
    {
      LOGGER.error("Authentication failed: {}", secureRemoteChannelRequest.getAuthentication());
      return null;
    }
  }
}
